/************************************************************
** @brief   : drv_epd
** @author  : vandoul
** @github  : https://gitee.com/vandoul
** @date    : 2022-01
** @version : v1.0.0
** @note    : drv_epd.c
***********************************************************/
#include "drv_epd.h"

#define EPD_DELAY(ms)                   rt_thread_delay(rt_tick_from_millisecond(ms))
#define EPD_SEND_COMMAND(dev, cmd)      do{uint8_t tmp=(cmd);rt_device_control(dev, EPD_IF_CMD_SEND_COMMAND, &tmp);}while(0)
#define EPD_SEND_DATA(dev, dat)         do{uint8_t tmp=(dat);rt_device_control(dev, EPD_IF_CMD_SEND_DATA, &tmp);}while(0)
#define EPD_WAIT_UNTIL_IDLE(dev)        do{rt_device_control(dev, EPD_IF_CMD_WAIT_UNTIL_IDLE, RT_NULL);}while(0)

static const uint8_t lut_update[EPD_LUT_UPDATE_MAX_NUM][30] = {
    {//full
        0x22, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x11,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
        0x01, 0x00, 0x00, 0x00, 0x00, 0x00
    },
    {//partial
        0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    }
};

void drv_epd_setlut(rt_device_t dev, int lut)
{
    dev->user_data = (void *)lut;
    EPD_SEND_COMMAND(dev, EPD_CMD_WRITE_LUT_REGISTER);
    for (int i = 0; i < 30; ++i) {
        EPD_SEND_DATA(dev, lut_update[lut][i]);
    }
}

void drv_epd_init(rt_device_t dev, int lut)
{
    rt_device_init(dev);
    rt_device_control(dev, EPD_IF_CMD_RESET, RT_NULL);
    EPD_SEND_COMMAND(dev, EPD_CMD_DRIVER_OUTPUT_CONTROL);
    EPD_SEND_DATA(dev, (EPD_HEIGHT - 1) & 0xFF);
    EPD_SEND_DATA(dev, ((EPD_HEIGHT - 1) >> 8) & 0xFF);
    EPD_SEND_DATA(dev, 0x00);
    EPD_SEND_COMMAND(dev, EPD_CMD_BOOSTER_SOFT_START_CONTROL);
    EPD_SEND_DATA(dev, 0xD7);
    EPD_SEND_DATA(dev, 0xD6);
    EPD_SEND_DATA(dev, 0x9D);
    EPD_SEND_COMMAND(dev, EPD_CMD_WRITE_VCOM_REGISTER);
    EPD_SEND_DATA(dev, 0xA8);
    EPD_SEND_COMMAND(dev, EPD_CMD_SET_DUMMY_LINE_PERIOD);
    EPD_SEND_DATA(dev, 0x1A);
    EPD_SEND_COMMAND(dev, EPD_CMD_SET_GATE_TIME);
    EPD_SEND_DATA(dev, 0x08);
    EPD_SEND_COMMAND(dev, EPD_CMD_BORDER_WAVEFORM_CONTROL);
    EPD_SEND_DATA(dev, 0x03);
    EPD_SEND_COMMAND(dev, EPD_CMD_DATA_ENTRY_MODE_SETTING);
    EPD_SEND_DATA(dev, 0x03);
    drv_epd_setlut(dev, lut);
    EPD_WAIT_UNTIL_IDLE(dev);
}

void drv_epd_set_memory_area(rt_device_t dev, int x_start, int y_start, int x_end, int y_end)
{
    EPD_SEND_COMMAND(dev, EPD_CMD_SET_RAM_X_ADDRESS_START_END_POSITION);
    /* x point must be the multiple of 8 or the last 3 bits will be ignored */
    EPD_SEND_DATA(dev, (x_start >> 3) & 0xFF);
    EPD_SEND_DATA(dev, (x_end >> 3) & 0xFF);
    EPD_SEND_COMMAND(dev, EPD_CMD_SET_RAM_Y_ADDRESS_START_END_POSITION);
    EPD_SEND_DATA(dev, y_start & 0xFF);
    EPD_SEND_DATA(dev, (y_start >> 8) & 0xFF);
    EPD_SEND_DATA(dev, y_end & 0xFF);
    EPD_SEND_DATA(dev, (y_end >> 8) & 0xFF);
}

void drv_epd_set_memory_pointer(rt_device_t dev, int x, int y)
{
    EPD_SEND_COMMAND(dev, EPD_CMD_SET_RAM_X_ADDRESS_COUNTER);
    /* x point must be the multiple of 8 or the last 3 bits will be ignored */
    EPD_SEND_DATA(dev, (x >> 3) & 0xFF);
    EPD_SEND_COMMAND(dev, EPD_CMD_SET_RAM_Y_ADDRESS_COUNTER);
    EPD_SEND_DATA(dev, y & 0xFF);
    EPD_SEND_DATA(dev, (y >> 8) & 0xFF);
//    EPD_WAIT_UNTIL_IDLE(dev);
}

void drv_epd_set_frame_memory(rt_device_t dev, const uint8_t* img_buf, int x, int y, int img_w, int img_h)
{
    int x_end;
    int y_end;

    if (img_buf == RT_NULL || x < 0 || img_w < 0 || y < 0 || img_h < 0) {
        return;
    }
    /* x point must be the multiple of 8 or the last 3 bits will be ignored */
    x &= 0xF8;
    img_w &= 0xF8;
    if (x + img_w >= EPD_WIDTH) {
        x_end = EPD_WIDTH - 1;
    } else {
        x_end = x + img_w - 1;
    }
    if (y + img_h >= EPD_HEIGHT) {
        y_end = EPD_HEIGHT - 1;
    } else {
        y_end = y + img_h - 1;
    }
    drv_epd_set_memory_area(dev, x, y, x_end, y_end);
    /* set the frame memory line by line */
    for (int j = y; j <= y_end; j++) {
        drv_epd_set_memory_pointer(dev, x, j);
        EPD_SEND_COMMAND(dev, EPD_CMD_WRITE_RAM);
        for (int i = x / 8; i <= x_end / 8; i++) {
            EPD_SEND_DATA(dev, img_buf[(i - x / 8) + (j - y) * (img_w / 8)]);
        }
    }
}

void drv_epd_clear_frame_memory(rt_device_t dev, uint8_t color)
{
    drv_epd_set_memory_area(dev, 0, 0, EPD_WIDTH - 1, EPD_HEIGHT - 1);
    for(int i=0; i<EPD_HEIGHT; i++) {
        drv_epd_set_memory_pointer(dev, 0, i);
        EPD_SEND_COMMAND(dev, EPD_CMD_WRITE_RAM);
        for (int j = 0; j < EPD_WIDTH/8; ++j) {
            EPD_SEND_DATA(dev, color);
        }
    }
}

void drv_epd_draw_color(rt_device_t dev, int x, int y, uint8_t color)
{
    drv_epd_set_frame_memory(dev, &color, x, y, 8, 1);
}

void drv_epd_display_frame(rt_device_t dev)
{
    EPD_SEND_COMMAND(dev, EPD_CMD_DISPLAY_UPDATE_CONTROL_2);
    EPD_SEND_DATA(dev, 0xC4);
    EPD_SEND_COMMAND(dev, EPD_CMD_MASTER_ACTIVATION);
    EPD_SEND_COMMAND(dev, EPD_CMD_TERMINATE_FRAME_READ_WRITE);
    EPD_WAIT_UNTIL_IDLE(dev);
}

void drv_epd_sleep(rt_device_t dev)
{
    EPD_SEND_COMMAND(dev, EPD_CMD_DEEP_SLEEP_MODE);
    EPD_SEND_DATA(dev, 0x01);
    EPD_WAIT_UNTIL_IDLE(dev);
}




